#include <cfloat>

#include <QPushButton>
#include <QGroupBox>
#include <QLineEdit>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QDoubleValidator>
#include <QRadioButton>
#include <QDebug>

#include "fuzzyeditor.h"
#include "systemcontrol.h"
#include "datacontroller.h"

FuzzyEditor::FuzzyEditor(QWidget *parent, SystemControl *system) :
    QWidget(parent),
    m_system(system)
{

    this->setWindowTitle("Fuzzy Set Editor");
    this->resize(800, 600);

    /* Set up the main panel so that Qt can handle the deleting of all children
     */
    QHBoxLayout* mainWindow = new QHBoxLayout(this);

    /* Add a set viewer to the window.
     */
    QVBoxLayout* leftPanel = new QVBoxLayout;

    m_viewer = new SetViewerPanel(&DataController::instance()->getInputOne());

    /* Connect the viewer to the slot that updates the text lines that show
     * the current values of all the points in the selected set, and add the
     * viewer to the left panel.
     */
    connect(m_viewer, SIGNAL(pointChanged()), this, SLOT(updatePointValues()));
    connect(m_viewer, SIGNAL(setChanged()), this, SLOT(updatePointValues()));
    connect(m_viewer, SIGNAL(setChanged()), this, SLOT(checkSelections()));

    leftPanel->addWidget(m_viewer, 10);

    /* Make the array of line editors that hold the current points values */
    m_point_values = new QLineEdit*[4];

    /* Set up the layouts that hold all the point editing text lines */
    QVBoxLayout* pointEditors[] = {new QVBoxLayout,
                                   new QVBoxLayout,
                                   new QVBoxLayout,
                                   new QVBoxLayout};

    /* Validator that ensures that only double values can be entered into the
     * text boxes.  This constructor has no effect on my machine.  It acts like
     * the default constructor.
     */
    QDoubleValidator* validator = new QDoubleValidator(-DBL_MAX,
                                                       DBL_MAX,
                                                       5,
                                                       this);

    /* Set up the four editor boxes and connect them to the slot that updates
     * the values of the points that belong to the current set.  Add them to
     * their own layouts
     */
    for ( int i = 0; i < 4; ++i ) {
        pointEditors[i]->addWidget(new QLabel(QString("Point %1").arg(i + 1)));
        m_point_values[i] = new QLineEdit;
        m_point_values[i]->setValidator(validator);

        connect(m_point_values[i], SIGNAL(textEdited(QString)),
                this, SLOT(setPointValues()));

        pointEditors[i]->addWidget(m_point_values[i]);
    }

    /* Set up the layout that contains the four point editors, then add it to
     * the left panel
     */
    QHBoxLayout* attributeRow = new QHBoxLayout;

    for ( int i = 0; i < 4; ++i )
        attributeRow->addLayout(pointEditors[i]);

    QGroupBox* pointEditorBox = new QGroupBox("Current Points");
    pointEditorBox->setLayout(attributeRow);

    leftPanel->addWidget(pointEditorBox, 1);

    /* Set up the column of radio buttons that let us select the set we're
     * currently editing in this window.  Set up their signals and slots, as
     * well as adding them to the layout that holds them.
     */
    QGroupBox* currentSets = new QGroupBox("Sets");
    QVBoxLayout* setSelector = new QVBoxLayout;
    QRadioButton* inputOneButton = new QRadioButton("Input One");
    QRadioButton* inputTwoButton = new QRadioButton("Input Two");
    QRadioButton* outputButton = new QRadioButton("Output");
    connect(inputOneButton, SIGNAL(clicked()), this, SLOT(setToInputOne()));
    connect(inputTwoButton, SIGNAL(clicked()), this, SLOT(setToInputTwo()));
    connect(outputButton, SIGNAL(clicked()), this, SLOT(setToOutput()));
    setSelector->addWidget(inputOneButton);
    setSelector->addWidget(inputTwoButton);
    setSelector->addWidget(outputButton);
    currentSets->setLayout(setSelector);

    /* Set the first input button to be selected because that is the set that
     * this window is initialized to display
     */
    inputOneButton->setChecked(true);

    /* Set up the radio button group that allows us to convert sets from one
     * type to another.  Make all the connections that set these to be properly
     * enabled or disabled based on the set that is currently selected.
     */
    QGroupBox* availableSets = new QGroupBox("Types");
    QVBoxLayout* setReplacer = new QVBoxLayout;
    QRadioButton* triangle = new QRadioButton("Triangle");
    QRadioButton* trapezoid = new QRadioButton("Trapezoid");

    /* This is all messy and I have no idea how it's going to work now that
     * there are 4 different MFs
     */

    /* When nothing is selected */
    connect(this, SIGNAL(noSelection(bool)),
            triangle, SLOT(setDisabled(bool)));
    connect(this, SIGNAL(noSelection(bool)),
            trapezoid, SLOT(setDisabled(bool)));

    /* When a triangle is selected */
    connect(triangle, SIGNAL(clicked()), this, SIGNAL(triangleSelected()));
    connect(this, SIGNAL(triangleSelected(bool)),
            triangle, SLOT(setDisabled(bool)));
    connect(this, SIGNAL(triangleSelected(bool)),
            triangle, SLOT(setChecked(bool)));
    connect(this, SIGNAL(triangleSelected(bool)),
            trapezoid, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(triangleSelected(bool)),
            this, SLOT(changeToTriangle(bool)));

    /* When a trapezoid is selected */
    connect(trapezoid, SIGNAL(clicked()), this, SIGNAL(trapezoidSelected()));
    connect(this, SIGNAL(trapezoidSelected(bool)),
            triangle, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(trapezoidSelected(bool)),
            trapezoid, SLOT(setChecked(bool)));
    connect(this, SIGNAL(trapezoidSelected(bool)),
            trapezoid, SLOT(setDisabled(bool)));
    connect(this, SIGNAL(trapezoidSelected(bool)),
            this, SLOT(changeToTrapezoid(bool)));

    /* Laying it all out */
    setReplacer->addWidget(triangle);
    setReplacer->addWidget(trapezoid);
    availableSets->setLayout(setReplacer);

    emit noSelection();

    /* Set up a column of buttons for whatever purpose they may hold */
    QPushButton* closeButton = new QPushButton("Close");
    connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
    closeButton->setToolTip("Close this window");

    /* Set up the right panel */
    QVBoxLayout* rightPanel = new QVBoxLayout;
    rightPanel->addWidget(currentSets);
    rightPanel->addWidget(availableSets);
    rightPanel->addStretch();
    rightPanel->addWidget(closeButton);

    mainWindow->addLayout(leftPanel);
    mainWindow->addLayout(rightPanel);
    this->setLayout(mainWindow);

 //   this->grabMouse(); // this crashes windows and Ubuntu works ok without it DAS
}

FuzzyEditor::~FuzzyEditor() {
    /* I'm pretty sure that Qt's QObject or QWidget framework takes care of
     * deleting everything that this object considers a child, so everything
     * should be deleted and leak free.
     */
}

void FuzzyEditor::updatePointValues() {

    BasicMembershipFunction* heldSet = m_viewer->getHeldSet();

    if(heldSet == 0) return;

    int size = heldSet->getSize();
    QPointF* points = heldSet->getPoints();

    for(int i = 0; i < size; ++i) {
        QString value;
        value.setNum(points[i].x());
        m_point_values[i]->setText(value);
    }

    if(size == 3) {
        m_point_values[3]->setText("");
        m_point_values[3]->setEnabled(false);
    }
    else if (size == 4)
        m_point_values[3]->setEnabled(true);
}

void FuzzyEditor::setPointValues() {

    BasicMembershipFunction* heldSet = m_viewer->getHeldSet();

    int size = heldSet->getSize();
    QPointF* points = heldSet->getPoints();

    for(int i = 0; i < size; ++i) {
        points[i].setX(m_point_values[i]->text().toDouble());
    }

    m_viewer->repaint();

}

/** Set the current set to the first input set */
void FuzzyEditor::setToInputOne() {

    this->m_viewer->setDisplayedCollection(&DataController::instance()
                                           ->getInputOne());
    this->clearSelections();
}

/** Set the current set to the second input set */
void FuzzyEditor::setToInputTwo() {

    this->m_viewer->setDisplayedCollection(&DataController::instance()
                                           ->getInputTwo());
    this->clearSelections();
}

/** Set the current set to the output set */
void FuzzyEditor::setToOutput() {

    this->m_viewer->setDisplayedCollection(&DataController::instance()
                                           ->getOutput());
    this->clearSelections();
}

void FuzzyEditor::checkSelections() {

    BasicMembershipFunction* function = m_viewer->getHeldSet();

    if( function == 0 ) {
        emit noSelection(true);
        return;
    }

    int selectedSize = function->getSize();

    if( selectedSize == 3) {
        emit triangleSelected(true);
    }
    else if( selectedSize == 4) {
        emit trapezoidSelected(true);
    }
}

void FuzzyEditor::clearSelections() {

    for(int i = 0; i < 4; ++i)
        m_point_values[i]->setText("");

    emit noSelection();
}

void FuzzyEditor::changeToTrapezoid(bool change) {

    if(change == false) return;

//    BasicMembershipFunction* oldFunction = m_viewer->getHeldSet();
//    qDebug() << *oldFunction;
//    BasicMembershipFunction* newFunction = m_system
//                                           ->getFuzzyController()
//                                           ->getTrapezoidMF(oldFunction);
//    qDebug() << *newFunction;

//    if(oldFunction == newFunction) return;

//    qDebug() << "Making a change";

//    Collection* collection = m_viewer->getDisplayedCollection();

//    int setPos = m_viewer->getHeldSetPos();

//    collection->changeMF(newFunction, setPos);
}

void FuzzyEditor::changeToTriangle(bool change) {

    if(change == false) return;

//    BasicMembershipFunction* oldFunction = m_viewer->getHeldSet();
//    qDebug() << *oldFunction;
//    BasicMembershipFunction* newFunction = m_system
//                                           ->getFuzzyController()
//                                           ->getTriangleMF(oldFunction);
//    qDebug() << *newFunction;

//    if(oldFunction == newFunction) return;

//    qDebug() << "Making a change";

//    Collection* collection = m_viewer->getDisplayedCollection();

//    int setPos = m_viewer->getHeldSetPos();

//    collection->changeMF(newFunction, setPos);
}
